package cn.jack.h264encodeanddecode

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.hardware.display.DisplayManager
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.DisplayMetrics
import android.view.Surface
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import cn.jack.h264encodeanddecode.ui.theme.H264EncodeAndDecodeTheme
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
import java.nio.ByteBuffer
import kotlin.properties.Delegates

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        checkPermission(this)

        mDisplayMetrics = DisplayMetrics()
        windowManager.defaultDisplay.getMetrics(mDisplayMetrics)
        mScreenWidth = mDisplayMetrics.widthPixels
        mScreenHeight = mDisplayMetrics.heightPixels

        setContent {
            H264EncodeAndDecodeTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting(this)
                }
            }
        }
    }

    @Deprecated(
        "Deprecated in Java", ReplaceWith(
            "super.onActivityResult(requestCode, resultCode, data)",
            "androidx.activity.ComponentActivity"
        )
    )
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode === CONST_REQUEST_CODE && resultCode === RESULT_OK) {
            data?.let {
                //1.获取到录屏的类
                mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data)
                initMediaCodec()
            }
        }
    }
}

private fun initMediaCodec() {
    try {
        //2.创建mediaCodec（编码）
        mMediaCodec = MediaCodec.createEncoderByType("video/avc")
        //宽高参数先写定
        val format = MediaFormat.createVideoFormat(
            MediaFormat.MIMETYPE_VIDEO_AVC,
            mScreenWidth, mScreenHeight
        )
        format.setInteger(
            MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
        )
        //在使用 MediaCodec 进行编码时，MediaFormat.KEY_FRAME_RATE 主要用于指定期望的编码帧率。设置这个值不会直接影响播放速度，而是告诉编码器生成视频帧的速率。
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 15)
        //码率
        format.setInteger(MediaFormat.KEY_BIT_RATE, 400000)
        //设置2s一个I帧
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2)
        mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        //3.获取mediaCodec的surface
        val surface: Surface = mMediaCodec.createInputSurface()
        object : Thread() {
            override fun run() {
                //4.开始编码
                mMediaCodec.start()
                //这是MediaCodec 提供的surface

                //提供的surface  与MediaProjection关联
                //5.创建一个虚拟显示，通常用于屏幕录制
                //参数1：虚拟显示的名称
                //参数2，3：虚拟显示的宽度和高度
                //参数4传入1：表示1dp = 1px（虚拟显示的每英寸点数）
                //参数5：标志位，指定创建虚拟显示的一些选项。
                //参数6：用于接收虚拟显示状态变化的回调接口
                //参数7：指定用于处理回调的 Handler
                mMediaProjection.createVirtualDisplay(
                    "screen-codec",
                    mScreenWidth, mScreenHeight, 1,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                    surface, null, null
                )
                val bufferInfo = MediaCodec.BufferInfo()
                while (true) {
                    //源源不断的插叙编码好的数据
                    //6.查询编码好的数据
                    val index: Int = mMediaCodec.dequeueOutputBuffer(bufferInfo, 100000)
                    println("index $index")
                    if (index >= 0) {
                        //7.获取对应的输出缓冲区，并从中读取解码后的数据
                        val buffer: ByteBuffer? = mMediaCodec.getOutputBuffer(index)
                        val outData = ByteArray(bufferInfo.size)
                        //8.将byteBuffer的数据交给ba数组
                        buffer?.get(outData)
                        //以字符串的方式写入
                        writeContent(outData)
                        //以字节的方式写入
                        writeBytes(outData)
                        mMediaCodec.releaseOutputBuffer(index, false)
                    }
                }
            }
        }.start()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

fun writeBytes(array: ByteArray?) {
    var writer: FileOutputStream? = null
    try {
        // 打开一个写文件器，构造函数中的第二个参数true表示以追加形式写文件
        writer = FileOutputStream(
            Environment.getExternalStorageDirectory().toString() + "/encoder_h264.h264", true
        )
        writer.write(array)
        writer.write('\n'.code)
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            writer?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
}

fun writeContent(array: ByteArray): String {
    val hexCharTable = charArrayOf(
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    )
    val sb = StringBuilder()
    for (b in array) {
        sb.append(hexCharTable[b.toInt() and 0xf0 shr 4])
        sb.append(hexCharTable[b.toInt() and 0x0f])
    }
    var writer: FileWriter? = null
    try {
        // 打开一个写文件器，构造函数中的第二个参数true表示以追加形式写文件
        writer =
            FileWriter(
                Environment.getExternalStorageDirectory().toString() + "/encoder_h264.txt",
                true
            )
        writer.write(sb.toString())
        writer.write("\n")
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            writer?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return sb.toString()
}

lateinit var mMediaProjectionManager: MediaProjectionManager
lateinit var mMediaProjection: MediaProjection
lateinit var mMediaCodec: MediaCodec
lateinit var mDisplayMetrics: DisplayMetrics

var mScreenWidth by Delegates.notNull<Int>()
var mScreenHeight by Delegates.notNull<Int>()
const val CONST_REQUEST_CODE = 0x001

fun checkPermission(activity: Activity): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(
            activity,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
        ) !== PackageManager.PERMISSION_GRANTED
    ) {
        ActivityCompat.requestPermissions(
            activity,
            arrayOf<String>(Manifest.permission.WRITE_EXTERNAL_STORAGE),
            1
        )
    }
    return false
}


@Composable
fun Greeting(activity: Activity) {
    Column {
        Button(
            onClick = {
                mMediaProjectionManager =
                    activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
                val captureIntent: Intent = mMediaProjectionManager.createScreenCaptureIntent()
                activity.startActivityForResult(captureIntent, CONST_REQUEST_CODE)
            },
            content = {
                Text(text = "H264编码")
            },
        )

        Button(
            onClick = {
                val intent = Intent(activity, DecodeActivity::class.java)
                activity.startActivity(intent)
            },
            content = {
                Text(text = "H264解码")
            },
        )
    }
}